#include "MainWindow.hpp"
#include "ui_MainWindow.h"

#include "QMultiComboBox.hpp"
#include "ReutersDataModel.hpp"
#include "ReutersDataView.hpp"
#include "ReutersDocument.hpp"

#include <QList>
#include <QFileDialog>
#include <QFile>

#include <utility>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    currentClassifier_(-1)
{
    ui->setupUi(this);
    searchBox_ = new QMultiComboBox ();
    ui->searchGroupLayout->addWidget(searchBox_);

    searchBox_->SetDisplayText("Nie wybrano żadnych klas.");

    connect (ui->load_select, SIGNAL(currentIndexChanged(int)), this, SLOT(selectPreloadedClsFile(int)));

    connect (searchBox_, SIGNAL(itemChanged()), this, SLOT(searchItemChanged()));

    connect (ui->documentList, SIGNAL(clicked(QModelIndex)), this, SLOT(showDocument(QModelIndex)));

    reutersLoader_.moveToThread(&workerThread_);

    connect (this, SIGNAL(loadClassification(QString)), &reutersLoader_, SLOT(loadClassification(QString)));
    connect (this, SIGNAL(loadDocument(ReutersDocument*)), &reutersLoader_, SLOT(loadDocument(ReutersDocument*)));
    connect (this, SIGNAL(filterData(QString)), &reutersLoader_, SLOT(loadFilter(QString)));

    connect (&reutersLoader_, SIGNAL(loadedDocument(ReutersDocument*)), this, SLOT(showDocument(ReutersDocument*)));
    connect (&reutersLoader_, SIGNAL(loadingClassification()), this, SLOT(disableUI()));
    connect (&reutersLoader_, SIGNAL(loadingClassification()), this, SLOT(onClsResLoading()));
    connect (&reutersLoader_, SIGNAL(loadedFilter(ReutersDataView*)), this, SLOT(viewUpdated(ReutersDataView*)));
    connect (&reutersLoader_, SIGNAL(loadedClassification(ReutersDataModel*)), this, SLOT(enableUI()));
    connect (&reutersLoader_, SIGNAL(loadedClassification(ReutersDataModel*)), this, SLOT(onClsResLoaded(ReutersDataModel*)));
    connect (&reutersLoader_, SIGNAL(clsAcc(float)), this, SLOT(setAcc(float)));

    workerThread_.start();
    selectPreloadedClsFile(0);
    showDocument (nullptr);
}


MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::setAcc (float acc) {
    ui->spin_cls_acc->setValue(acc);
}


bool MainWindow::loadClsFromFile(QString file_name) {
    QFile file(file_name);
    if (!file.exists()) {
        file_name= "../" + file_name;
        file.setFileName(file_name);
    }
    if (!file.exists())
        return false;
    ui->documentContent->setModel(nullptr);
    ui->documentList->setModel(nullptr);
    emit loadClassification(file_name);
    ui->label_cls_file->setText(file.fileName());
    return true;
}


bool MainWindow::loadClsFile () {
    QString file_name = QFileDialog::getOpenFileName(this, "Wczytaj plik zawierający klasyfikację", "Pliki zawierające klasyfikację (*.txt)");
    if (file_name.isNull()) return false;
    return loadClsFromFile(file_name);
}


void MainWindow::selectPreloadedClsFile(int i) {
    if (i != 3 && currentClassifier_ == i) return;
    int old_classifier = currentClassifier_;
    currentClassifier_ = i;
    switch (i) {
    case 0:
        loadClsFromFile("Naive Bayes.txt");
        break;
    case 1:
        loadClsFromFile("kNN.txt");
        break;
    case 2:
        loadClsFromFile("SVM.txt");
        break;
    case 3:
        if (!loadClsFile())
            currentClassifier_ = old_classifier;
        break;
    }
}


inline QString JoinStrings(const QList<QString>& strings) {
    QString result;
    bool not_first_time = false;
    for (QString cls : strings) {
        result.append((not_first_time ? ";" : "") + cls);
        not_first_time = true;
    }
    return result;
}


void MainWindow::searchItemChanged () {
    QList<QString> new_filter;
    for (int i = 0; i != searchBox_->count(); ++i) {
        if (searchBox_->itemData(i).toBool())
            new_filter.push_back(searchBox_->itemText(i));
    }
    if (new_filter.empty()) {
        searchBox_->SetDisplayText("Nie wybrano żadnych klas.");
    } else {
        QString info_label = "Wybrane klasy: ";
        bool not_first_time = false;
        for (QString cls : new_filter) {
            info_label.append((not_first_time ? ", " : " ") + cls);
            not_first_time = true;
        }
        searchBox_->SetDisplayText(info_label);
    }
    emit filterData(JoinStrings(new_filter));
}


void MainWindow::viewUpdated (ReutersDataView* model) {
    ui->documentList->setModel(nullptr);
    model->setParent(ui->documentList);
    ui->documentList->setModel(model);
    if (model->contains(currentDoc_))
        showDocument (currentDoc_);
    else
        showDocument (nullptr);
    int docs_total = model->docsTotal();
    int docs_filtered = model->docsFiltered();
    if (docs_filtered != docs_total)
        ui->statusBar->showMessage(QString("Wyświetlanie %1 z %2 depesz agencyjnych.").arg(docs_filtered).arg(docs_total));
    else
        ui->statusBar->showMessage(QString("Wyświetlanie %1 depesz agencyjnych.").arg(docs_total));
}


void MainWindow::disableUI (bool) {
    ui->searchGroup->setEnabled(false);
    ui->documentList->setEnabled(false);
}


void MainWindow::disableUI () {
    disableUI (true);
}


void MainWindow::enableUI () {
    ui->searchGroup->setEnabled(true);
    ui->documentList->setEnabled(true);
}


void MainWindow::showDocument (QModelIndex i) {
    ReutersDocument* d = dynamic_cast<ReutersDataView*>(ui->documentList->model())->getDocAt(i);
    if (!d->loaded()) {
        emit loadDocument(d);
    } else {
        loadDocument(d);
    }
}


void MainWindow::showDocument (ReutersDocument* d) {
    currentDoc_ = d;
    ui->documentContent->setModel(nullptr);
    if (d != nullptr) {
        ui->documentContent->setModel(d);
    } else {
        ui->documentContent->setModel(&ReutersDocument::NULL_DOC);
    }
}


void MainWindow::onClsResLoading () {
    ui->statusBar->showMessage("Ładowanie wyników klasyfikacji...");
    searchBox_->clear();
    showDocument (nullptr);
    searchBox_->SetDisplayText("Nie wybrano żadnych klas.");
}


void MainWindow::onClsResLoaded (ReutersDataModel* rdm) {
    for (const QString& cls : rdm->getClasses()) {
        searchBox_->addItem(cls);
    }
}
